<?php
declare(strict_types=1);

namespace ServerApp\lib\server\conf;

use Exception;
use ServerApp\lib\core\STcpServer;
use ServerApp\lib\process\SGlobal;
use ServerApp\lib\process\SProcess;
use ServerApp\lib\server\model\redis\ConfClient;
use ServerApp\lib\server\model\redis\ConfServer;
use Swoole\Server;
use WebApp\lib\config\Config;
use ServerApp\lib\server\model\swoole\ClientList;

/**
 * Class Conf
 */
class Conf extends STcpServer
{
    const SPLIT = "\e";
    const HEART_MSG = "\t";
    private $first = true;
    private $func = null;

    /**
     * Conf constructor.
     * @param IConf $func1
     */
    public function __construct(IConf $func1)
    {
        $this->func = $func1;
        parent::__construct();
    }

    /**
     * @return string
     */
    protected function getMaster(): string
    {
        return $this->func->getName() . "_m1";
    }

    /**
     * @return string
     */
    protected function getManager(): string
    {
        return $this->func->getName() . "_m2";
    }

    /**
     * @return string
     */
    protected function getWorker(): string
    {
        return $this->func->getName() . "_w";
    }

    /**
     * @return int
     */
    public function getPort(): int
    {
        return PORT;
    }

    /**
     * @return string
     */
    protected function getBind(): string
    {
        return '0.0.0.0';
    }

    /**
     * @return array
     * @throws \ErrorException
     */
    protected function getSetting(): array
    {
        return array_merge(
            Config::getConf('server.setting'),
            Config::getConf('server.children.' . SERVER_NAME),
            [
                'open_eof_split' => true,
                'package_eof' => self::SPLIT,
            ]
        );
    }

    protected function Start(): void
    {
        tracker(I, 'start');
    }

    protected function ManagerStart(): void
    {
        tracker(I, 'manager start');
    }

    /**
     *
     */
    private function isFirst(){
        tracker(I, 'isFirst');
        tracker(I,  (int)$this->first);
        if (!ClientList::getInstance()->getRow("first")) {
            ClientList::getInstance()->getRow("first", [
                ClientList::TYPE=> 1
            ]);
            $this->first = false;
            return true;
        }
        return false;
    }

    /**
     * @param $server
     * @param $worker_id
     */
    protected function WorkerStart(Server $server, $worker_id): void
    {
        tracker(I, 'WorkerStart');
        if ($worker_id === 0) {
            $this->intervalHeartBeat();
            if($this->isFirst()) {
                ConfServer::getInstance()->hSet($this->getHost(), '1');
                ConfClient::getInstance()->del();
            }
        }
//        $tmp = new stdClass();
//        $tmp->func = function() use($tmp){
//            while ($data = Chan::getInstance()->pop()) {
//                list($host, $str) = explode("\v", $data);
//                RedisPool::getInstance()->exec(function(Redis $client) use ($host, $str){
//                    tracker(D, $host);
//                    tracker(D, $str);
//                    $host = crc32($host);
//                    $key = "list_{$host}";
//                    $client->lPush($key, "$str\v". (time() + 20));
//                });
//            }
//            swoole_timer_after(1000, $tmp->func);
//        };
//        ($tmp->func)();
    }

    private function intervalHeartBeat()
    {
        tracker(I, __FUNCTION__);
        ($heartBeatManager = new SProcess(function () {
            swoole_event_add(SGlobal::getInstance()->process->pipe, function () {
                $key = SGlobal::getInstance()->process->read();
                if ($key !== 'start') {
                    return;
                }
                if ($this->server->connection_list()) {
                    foreach ($this->server->connection_list() as $fd) {
                        $this->server->send($fd, self::HEART_MSG . self::SPLIT);
                    }
                }
                SGlobal::getInstance()->process->write(SGlobal::getInstance()->process->pid . '');
            });
        }, false, 2))->start();
        swoole_event_add($heartBeatManager->pipe, function () use ($heartBeatManager) {
            $pid = (int)$heartBeatManager->read();
            if ($pid === $heartBeatManager->pid) {
                $heartBeatManager->idel();
            }
        });

        try {
            $setting = $this->getSetting();
            $second = (int)$setting['heartbeat_check_interval'] ?? 300;
            swoole_timer_tick($second * 1000, function () use ($heartBeatManager) {
                if (!$heartBeatManager->isUsing()) {
                    $heartBeatManager->work('start');
                }
            });
        } catch (\ErrorException $e) {
        }
    }

    /**
     * @param $server
     * @param $worker_id
     */
    protected function WorkerStop(Server $server, $worker_id): void
    {
        tracker(I, "$worker_id : WorkerStop");
        if ($worker_id === 0) {
            foreach (SGlobal::getInstance()->child_process_pids as $pid => $noUse) {
                SProcess::kill($pid);
            }
        }
    }

    public function __destruct()
    {
        foreach (SGlobal::getInstance()->child_process_pids as $pid => $noUse) {
            SProcess::kill($pid);
        }
        unset($this->master);
        unset($this->forwardTasks);
        unset($this->forwardUsing);
    }

    /**
     * @param $server
     * @param $fd
     */
    protected function Close(Server $server, $fd): void
    {
        tracker(I, "Client close fd: $fd");
        ConfStation::getInstance()->close((string)$fd);
    }

    /**
     * @param $server
     * @param $fd
     */
    protected function Connect(Server $server, $fd): void
    {
        tracker(I, "Client{$fd}:Connect");
    }

    /**
     * @param Server $server
     * @param $fd
     * @param $from_id
     * @param $data
     * @throws \Exception
     */
    protected function Receive(Server $server, $fd, $from_id, $data): void
    {
        unset($from_id);
        try {
            if (!$data) {
                throw new Exception();
            }

            $data = substr($data, 0, -1);

            if ($data === self::HEART_MSG) {
                return;
            } else {
                if ($data) {
                    tracker(D, "conf receive: $data");
                    ConfStation::getInstance()->decodeMsg($data, (string)$fd);
                } else {
                    throw new Exception();
                }
            }
        } catch (Exception $e) {
            $server->close($fd);
        }
    }

    protected function getService()
    {
        // TODO: Implement getService() method.
    }
}
